From: Didier Roche Date: Thu, 1 Nov 2018 12:55:17 +0000 (+0100) Subject: extensionPrefs: Switch to D-Bus API to get extension live state X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~845^2^2~4 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=9928125e7d584ed45cdde8291cb62a607e73696a;p=gnome-shell.git extensionPrefs: Switch to D-Bus API to get extension live state By direclty using the underlying GSetting, whether or not an extension appears as enabled or disabled currently depends only on whether it is included in the 'enabled-extensions' list or not. However this doesn't necessarily reflect the real extension state, as an extension may be in error state, or enabled via the session mode. Switch to the extensions D-Bus API to ensure that the list of extensions and each extension's state correctly reflects the state in gnome-shell. https://bugzilla.gnome.org/show_bug.cgi?id=789852 --- diff --git a/js/extensionPrefs/main.js b/js/extensionPrefs/main.js index 0f0b4b06f..e5961e2e0 100644 --- a/js/extensionPrefs/main.js +++ b/js/extensionPrefs/main.js @@ -8,6 +8,8 @@ const Config = imports.misc.config; const ExtensionUtils = imports.misc.extensionUtils; const { loadInterfaceXML } = imports.misc.fileUtils; +const { ExtensionState } = ExtensionUtils; + const GnomeShellIface = loadInterfaceXML('org.gnome.Shell.Extensions'); const GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface); @@ -30,6 +32,11 @@ var Application = GObject.registerClass({ this._startupUuid = null; this._loaded = false; this._skipMainWindow = false; + this._shellProxy = null; + } + + get shellProxy() { + return this._shellProxy; } _showPrefs(uuid) { @@ -223,10 +230,8 @@ var Application = GObject.registerClass({ this._mainStack.add_named(new EmptyPlaceholder(), 'placeholder'); this._shellProxy = new GnomeShellProxy(Gio.DBus.session, 'org.gnome.Shell', '/org/gnome/Shell'); - this._shellProxy.connectSignal('ExtensionStateChanged', (proxy, senderName, [uuid, state]) => { - if (ExtensionUtils.extensions[uuid] !== undefined) - this._scanExtensions(); - }); + this._shellProxy.connectSignal('ExtensionStateChanged', + this._onExtensionStateChanged.bind(this)); this._window.show_all(); } @@ -243,14 +248,51 @@ var Application = GObject.registerClass({ row.set_header(sep); } + _findExtensionRow(uuid) { + return this._extensionSelector.get_children().find(c => c.uuid === uuid); + } + + _onExtensionStateChanged(proxy, senderName, [uuid, newState]) { + let row = this._findExtensionRow(uuid); + if (row) { + let { state } = ExtensionUtils.deserializeExtension(newState); + if (state == ExtensionState.UNINSTALLED) + row.destroy(); + return; // we only deal with new and deleted extensions here + } + + this._shellProxy.GetExtensionInfoRemote(uuid, ([serialized]) => { + let extension = ExtensionUtils.deserializeExtension(serialized); + if (!extension) + return; + // check the extension wasn't added in between + if (this._findExtensionRow(uuid) != null) + return; + this._addExtensionRow(extension); + }); + } + _scanExtensions() { - let finder = new ExtensionUtils.ExtensionFinder(); - finder.connect('extension-found', this._extensionFound.bind(this)); - finder.scanExtensions(); - this._extensionsLoaded(); + this._shellProxy.ListExtensionsRemote(([extensionsMap], e) => { + if (e) { + if (e instanceof Gio.DBusError) { + log(`Failed to connect to shell proxy: ${e}`); + this._mainStack.add_named(new NoShellPlaceholder(), 'noshell'); + this._mainStack.visible_child_name = 'noshell'; + } else + throw e; + return; + } + + for (let uuid in extensionsMap) { + let extension = ExtensionUtils.deserializeExtension(extensionsMap[uuid]); + this._addExtensionRow(extension); + } + this._extensionsLoaded(); + }); } - _extensionFound(finder, extension) { + _addExtensionRow(extension) { let row = new ExtensionRow(extension); row.prefsButton.connect('clicked', () => { @@ -471,6 +513,35 @@ class EmptyPlaceholder extends Gtk.Box { } }); +var NoShellPlaceholder = GObject.registerClass( +class NoShellPlaceholder extends Gtk.Box { + _init() { + super._init({ + orientation: Gtk.Orientation.VERTICAL, + spacing: 12, + margin: 100, + margin_bottom: 60 + }); + + let label = new Gtk.Label({ + label: '%s'.format( + _("Something’s gone wrong")), + use_markup: true + }); + label.get_style_context().add_class(Gtk.STYLE_CLASS_DIM_LABEL); + this.add(label); + + label = new Gtk.Label({ + label: _("We’re very sorry, but it was not possible to get the list of installed extensions. Make sure you are logged into GNOME and try again."), + justify: Gtk.Justification.CENTER, + wrap: true + }); + this.add(label); + + this.show_all(); + } +}); + var DescriptionLabel = GObject.registerClass( class DescriptionLabel extends Gtk.Label { vfunc_get_preferred_height_for_width(width) { @@ -486,22 +557,23 @@ class ExtensionRow extends Gtk.ListBoxRow { _init(extension) { super._init(); + this._app = Gio.Application.get_default(); this._extension = extension; this._prefsModule = null; - this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); - this._settings.connect('changed::enabled-extensions', () => { - this._switch.state = this._isEnabled(); - }); - this._settings.connect('changed::disable-extension-version-validation', - () => { - this._switch.sensitive = this._canEnable(); - }); - this._settings.connect('changed::disable-user-extensions', - () => { - this._switch.sensitive = this._canEnable(); + this._extensionStateChangedId = this._app.shellProxy.connectSignal( + 'ExtensionStateChanged', (p, sender, [uuid, newState]) => { + if (this.uuid !== uuid) + return; + + this._extension = ExtensionUtils.deserializeExtension(newState); + let state = (this._extension.state == ExtensionState.ENABLED); + this._switch.state = state; + this._switch.sensitive = this._canToggle(); }); + this.connect('destroy', this._onDestroy.bind(this)); + this._buildUI(); } @@ -521,6 +593,15 @@ class ExtensionRow extends Gtk.ListBoxRow { return this._extension.metadata.url; } + _onDestroy() { + if (!this._app.shellProxy) + return; + + if (this._extensionStateChangedId) + this._app.shellProxy.disconnectSignal(this._extensionStateChangedId); + this._extensionStateChangedId = 0; + } + _buildUI() { let hbox = new Gtk.Box({ orientation: Gtk.Orientation.HORIZONTAL, hexpand: true, margin_end: 24, spacing: 24, @@ -554,50 +635,23 @@ class ExtensionRow extends Gtk.ListBoxRow { this.prefsButton = button; - this._switch = new Gtk.Switch({ valign: Gtk.Align.CENTER, - sensitive: this._canEnable(), - state: this._isEnabled() }); + this._switch = new Gtk.Switch({ + valign: Gtk.Align.CENTER, + sensitive: this._canToggle(), + state: this._extension.state === ExtensionState.ENABLED + }); this._switch.connect('notify::active', () => { if (this._switch.active) - this._enable(); + this._app.shellProxy.EnableExtensionRemote(this.uuid); else - this._disable(); + this._app.shellProxy.DisableExtensionRemote(this.uuid); }); this._switch.connect('state-set', () => true); hbox.add(this._switch); } - _canEnable() { - let checkVersion = !this._settings.get_boolean('disable-extension-version-validation'); - - return !this._settings.get_boolean('disable-user-extensions') && - !(checkVersion && ExtensionUtils.isOutOfDate(this._extension)); - } - - _isEnabled() { - let extensions = this._settings.get_strv('enabled-extensions'); - return extensions.includes(this.uuid); - } - - _enable() { - let extensions = this._settings.get_strv('enabled-extensions'); - if (extensions.includes(this.uuid)) - return; - - extensions.push(this.uuid); - this._settings.set_strv('enabled-extensions', extensions); - } - - _disable() { - let extensions = this._settings.get_strv('enabled-extensions'); - let pos = extensions.indexOf(this.uuid); - if (pos == -1) - return; - do { - extensions.splice(pos, 1); - pos = extensions.indexOf(this.uuid); - } while (pos != -1); - this._settings.set_strv('enabled-extensions', extensions); + _canToggle() { + return this._extension.canChange; } get prefsModule() {